﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace DataLeak
{
    public class DataLeak : IPlugin.IPlugin
    {
        #region InfoPlugin

        public string[] Authors
        {
            get
            {
                return new string[] { "Manuel Fernandez" };
            }
        }

        public string Name
        {
            get
            {
                return "DataLeak";
            }
        }

        public string Description
        {
            get
            {
                return "Detects data leaks in the server responses. In case a leak is detected the packet is dropped.";
            }
        }

        public IPlugin.PluginRing Ring
        {
            get
            {
                return IPlugin.PluginRing.Ring2;
            }
        }
        #endregion

        #region rules
        private string[] outputRules = 
        { 
            @"[a-z]:\\inetpub\b", // IIS default location
            @"\<b\>Version Information\:\<\/b\>(?:&nbsp;|\s)Microsoft \.NET Framework Version\:",
            @">error \'ASP\b", //IIS Errors leakage
            @"\berror \'800",
            @"\<b\>Version Information\:\<\/b\>(?:&nbsp;|\s)ASP\.NET Version\:",
            @"\bA trappable error occurred in an external object\. The script cannot continue running\b",
            @"\bMicrosoft VBScript runtime Error\b",
            @"\bMicrosoft VBScript compilation \(0x8\b",
            @"/[Ee]rror[Mm]essage\.aspx\?[Ee]rror\b",
            @"\bMicrosoft VBScript runtime \(0x8\b",
            @"\bObject required\: \'",
            @"\bADODB\.Command\b.{0,100}?\bApplication uses a value of the wrong type for the current operation\b",
            @"/[Ee]rror[Mm]essage\.asp\?[Ee]rror\b",
            @"\bADODB\.Command\b.{0,100}?\berror\'",
            @"\bMicrosoft VBScript compilation error\b",
            @"\bServer Error in.{0,50}?\bApplication\b",
            @"\b403 Forbidden\b.*?\bInternet Security and Acceleration Server\b", // ISA server existence revealed
            @"<b>Warning<\/b>.{0,100}?:.{0,1000}?\bon line\b",// PHP Information Leakage
            @"<b>Fatal error<\/b>.{0,100}?:.{0,1000}?\bon line\b",// PHP Information Leakage
            @"<b>Notice<\/b>.{0,100}?:.{0,1000}?\bon line\b",// PHP Information Leakage
            @"<h2>Site Error<\/h2>.{0,20}<p>An error was encountered while publishing this resource\.",// Zope Information Leakage
            @"<\%", //Fuga de codigo ASP o JSP
            @"<cf",// Fuga de codigo coldfusion
            @"<\?(?!xml)",// Fuga codigo php
            @"(?:Microsoft OLE DB Provider for SQL Server(?:<\/font>.{1,20}?error '800(?:04005|40e31)'.{1,40}?Timeout expired| \(0x80040e31\)<br>Timeout expired<br>)|<h1>internal server error<\/h1>.*?<h2>part of the server has crashed or it has a configuration error\.<\/h2>|cannot connect to the server: timed out)",// The application is not available
            @"<title>JSP compile error<\/title>",// Weblogic information disclosure
            "href\\s?=[\\s\\\"\\']*[A-Za-z]\\:\\x5c([^\\\"\\']+)", // File or Directory Names Leakage (ejpl: href="c:\archivo.txt"
            @"\bCould not find server \'\w+\' in sysservers\. execute sp_addlinkedserver\b",// SQL Errors leakage
            @"\bSyntax error converting the \w+ value .*? to a column of data type\b",
            @"\bORA-\d{5}\: |ORA-[0-9][0-9][0-9][0-9]|Oracle error|Oracle.*Driver|Warning.*Woci_.*|Warning.*Wora_.*",
            @"\bUnclosed quotation mark before the character string\b",
            @"\[Microsoft\]\[ODBC |Driver.* SQL[-_ ]*Server|OLE DB.* SQL Server|(W|A)SQL Server.*Driver|Warning.*mssql_.*|(W|A)SQL Server.*[0-9a-fA-F]{8}|Exception Details:.*WSystem.Data.SqlClient.|Exception Details:.*WRoadhouse.Cms.",
            @"\berror \'800a01b8\'",
            @"\bYou have an error in your SQL syntax near \'",
            @"\bmicrosoft jet database engine error \'8|Microsoft Access Driver|JET Database Engine|Access Database Engine",
            @"\bselect list because it is not contained in an aggregate function and there is no GROUP BY clause\b",
            @"\bUnable to connect to PostgreSQL server\:",
            @"\bPostgreSQL query failed\:|PostgreSQL.*ERROR|Warning.*Wpg_.*|valid PostgreSQL result|Npgsql.",
            @"\bsupplied argument is not a valid MS SQL\b",
            @"\bsupplied argument is not a valid Oracle\b",
            @"\bWarning: mysql_connect\(\)\:",
            @"\bsupplied argument is not a valid ODBC\b",
            @"\bMicrosoft OLE DB Provider for .{0,30} [eE]rror '",
            @"\bSQL Server does not exist or access denied\b",
            @"\bEither BOF or EOF is True, or the current record has been deleted(. Requested|; the operation)\b",
            @"\bcannot take a \w+ data type as an argument\.",
            @"\bselect list because it is not contained in either an aggregate function or the GROUP BY clause\b",
            @"\bThe column prefix .{0,50}? does not match with a table name or alias name used in the query\b",
            @"\bsupplied argument is not a valid PostgreSQL result\b",
            @"\bYou have an error in your SQL syntax",
            @"\bsupplied argument is not a valid MySQL\b",
            @"SQL syntax.*MySQL|Warning.*mysql_.*|valid MySQL result|MySqlClient.|SQLite/JDBCDriver|SQLite.Exception|System.Data.SQLite.SQLiteException|Warning.*sqlite_.*|Warning.*SQLite3::",
            @"\bincorrect syntax near (?:\'|the\b|\@\@error\b)"
        };
        #endregion

        public Model.Elements.HttpPacket Start(Model.Elements.HttpPacket packet)
        {
            if (packet is Model.Elements.HttpResponsePacket)
            {
                Model.Elements.HttpResponsePacket response = packet as Model.Elements.HttpResponsePacket;

                Model.Elements.HttpHeaderContentType contentType = response.GetContentType();

                if (contentType == null)
                    return packet;

                // Process leaks just in not binary responses, and with a content-type
                if (IPlugin.API.General.IsBinaryContentType(contentType))
                    return packet;

                // This shoudln't happend never because the ResponseParser decompress the packets 
                // and removes the Content-Encoding, but for if needed let's do another check
                if (response.ExistHeader("Content-Encoding", false))
                    return packet;

                string sData = ASCIIEncoding.ASCII.GetString(response.data);
                
                bool modified = false;
                
                bool matches = false;
                do
                {
                    matches = false;

                    foreach (string rule in outputRules)
                    {
                        Match c = Regex.Match(sData, rule, RegexOptions.None);
                        if (c.Success)
                        {
                            sData = sData.Replace(c.Value, "<b>[Data leak censured by proxy]</b>");
                            matches = true;
                            modified = true;
                            //response.dropped = true;
                            //return response;
                        }
                    }

                } while (matches);

                if (modified)
                {
                    response.data = ASCIIEncoding.ASCII.GetBytes(sData);
                    response.RemoveHeader("Content-Length", false);
                    response.AddHeader(new Model.Elements.HttpHeader("Content-Length", response.data.Length.ToString()));

                    return response;
                }
            }

            return packet;
        }

       
    }
}
